package com.hmy.finance.service.core.dto;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hmy.finance.api.bo.core.request.TerminationRentalRequest;
import com.hmy.finance.api.bo.core.request.enums.FeeTypeEnum;
import com.hmy.finance.api.bo.core.request.vos.WaiverRuleVo;
import com.hmy.finance.api.bo.core.response.TerminationRentalResponse;
import com.hmy.finance.api.common.enums.RechargeTypeEnum;
import com.hmy.finance.common.enums.BusinessBillTypeEnum;
import com.hmy.finance.common.enums.ProfitLossStatusEnum;
import com.hmy.finance.dal.po.*;
import com.hmy.finance.dal.po.json.ShippingInfoJson;
import com.hmy.finance.dal.po.json.WaiverRuleJson;
import com.hmy.finance.service.core.dto.vos.DeviceInfoVo;
import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 终止租赁请求业务逻辑处理参数
 *
 * @author songkf
 */
@Data
public class TerminationRentalDto implements Serializable {
    private static final long serialVersionUID = 7457976635035196874L;
    /**
     * 客户ID
     */
    private Long customerId;
    /**
     * 合同ID
     */
    private Long contractId;
    /**
     * 退场单ID
     */
    private Long exitFormId;
    /**
     * 退租时间
     */
    private LocalDateTime terminationTime;
    /**
     * 退场运费
     */
    private BigDecimal exitShippingAmount;
    /**
     * 退场设备总月租价格
     */
    private BigDecimal totalRentalMonthlyPrice = BigDecimal.ZERO;
    /**
     * 退场运费减免
     */
    private List<WaiverRuleVo> exitShippingFeeWaiverList = new ArrayList<>();
    /**
     * 退场设备信息集合
     */
    private List<DeviceInfoVo> exitDeviceInfoList = new ArrayList<>();

    /**
     * 正在租赁的设备集合
     */
    private List<DeviceRentalRecordPo> rentingDeviceList = new ArrayList<>();
    /**
     * 损益业务记录
     */
    private List<ProfitLossRecordPo> profitLossRecordPoList = new ArrayList<>();
    /**
     * 设备损益明细记录
     */
    private List<DeviceProfitLossDetailPo> deviceProfitLossDetailPoList = new ArrayList<>();
    ;
    /**
     * 本次退场涉及的进场单ID集合
     */
    private List<Long> entryFormIdList = new ArrayList<>();
    /**
     * 已全部退场的进场单ID集合
     */
    private List<Long> allExitedEntryFormIdList = new ArrayList<>();
    /**
     * 待处理的进场运费减免业务记录以及设备改装费减免业务记录
     */
    private List<ProfitLossRecordPo> pendingProfitLossRecordList = new ArrayList<>();
    /**
     * 待处理的设备进场运费减免损益记录以及设备改装费减免损益记录
     */
    private List<DeviceProfitLossDetailPo> pendingDeviceProfitLossDetailList = new ArrayList<>();
    /**
     * 待处理的设备日费用流水
     */
    private List<DeviceDailyBillPo> pendingDeviceDailyBillList = new ArrayList<>();
    private List<ContractDailyBillPo> pendingContractDailyBillList = new ArrayList<>();

    private List<Long> pendingRecoveryProfitLossRecordIdList = new ArrayList<>();
    /**
     * 待恢复的设备损益明细记录
     */
    private List<Long> pendingRecoveryDeviceProfitLossDetailIdList = new ArrayList<>();

    /**
     * 构建终止租赁请求业务逻辑处理参数
     *
     * @param request 终止租赁请求
     * @return 终止租赁请求业务逻辑处理参数
     * @author songkf
     */
    public static TerminationRentalDto buildTerminationRentalDto(TerminationRentalRequest request) {
        TerminationRentalDto dto = new TerminationRentalDto();
        dto.setContractId(request.getContractId());
        dto.setExitFormId(request.getExitFormId());
        dto.setTerminationTime(request.getTerminationTime());
        dto.setExitShippingAmount(request.getExitShippingAmount());
        dto.setCustomerId(request.getCustomerId());
        //使用BeanUtil进行List拷贝，并赋值给exitDeviceInfoList
        dto.setExitDeviceInfoList(BeanUtil.copyToList(request.getExitDeviceInfoList(), DeviceInfoVo.class));
        return dto;
    }

    /**
     * 获取终止租赁响应
     *
     * @return 结果
     * @author songkf
     */
    public TerminationRentalResponse getResponse() {
        TerminationRentalResponse response = new TerminationRentalResponse();
        response.setResult(Boolean.TRUE);
        return response;
    }

    /**
     * 获取租赁设备列表查询条件
     *
     * @return 查询条件
     * @author songkf
     */
    public LambdaQueryWrapper<DeviceRentalRecordPo> getRentingDeviceListQueryWrapper() {
        //使用Wrappers构建LambdaQueryWrapper，使用contractIdId以及exitDeviceInfoList的deviceId集合进行查询
        return Wrappers.lambdaQuery(DeviceRentalRecordPo.class).eq(DeviceRentalRecordPo::getContractId, contractId)
                .and(wrapper -> {
                    exitDeviceInfoList.forEach(deviceInfoVo -> {
                                wrapper.or(
                                        wra -> wra.eq(DeviceRentalRecordPo::getDeviceId, deviceInfoVo.getDeviceId())
                                                .eq(DeviceRentalRecordPo::getEntryFormId, deviceInfoVo.getEntryFormId())
                                );
                            }
                    );
                });
    }

    /**
     * 批量设置租赁设备退租时间
     *
     * @author songkf
     */
    public void batchSetRentingDevicesTerminationTime(DefaultIdentifierGenerator identifierGenerator) {
        //遍历设置rentingDeviceList的terminationTime

    }

    private static BigDecimal getExitPauseWaiverAmount(DeviceRentalRecordPo deviceRentalRecordPo) {
        BigDecimal remainingPauseDays = BigDecimal.valueOf(deviceRentalRecordPo.getTotalRemainingPauseDays());
        BigDecimal pauseWaiverFee = remainingPauseDays.multiply(deviceRentalRecordPo.getDailyRentalPrice());
        //如果已使用月租金费用
        if (deviceRentalRecordPo.getUseMonthlyRentalPrice()) {
            //使用月租金单价计算
            pauseWaiverFee = remainingPauseDays.multiply(deviceRentalRecordPo.getMonthlyRentalPrice()).divide(BigDecimal.valueOf(30), 5, RoundingMode.HALF_UP);
        }
        return pauseWaiverFee;
    }

    /**
     * 通过entryFormInfo构建ShippingInfoJson对象
     *
     * @return ShippingInfoJson对象
     * @author songkf
     */
    public ShippingInfoJson acceptShippingInfo() {
        ShippingInfoJson shippingInfoJson = new ShippingInfoJson();
        shippingInfoJson.setShippingAllAmount(this.getExitShippingAmount());
        shippingInfoJson.setShippingWaiverRuleList(this.getExitShippingFeeWaiverList().stream().map(waiverRuleVo -> {
            WaiverRuleJson waiverRuleJson = new WaiverRuleJson();
            waiverRuleJson.setWaiverDays(waiverRuleVo.getWaiverDays());
            waiverRuleJson.setWaiverRate(waiverRuleVo.getWaiverRate());
            return waiverRuleJson;
        }).collect(Collectors.toList()));
        return shippingInfoJson;
    }

    /**
     * 组装退场运费损益业务记录
     *
     * @param identifierGenerator ID生成器
     * @author songkf
     */
    public void assembleShippingFeeProfitLossRecord(DefaultIdentifierGenerator identifierGenerator) {
        //构建进场运费损益业务记录
        ProfitLossRecordPo entryShippingProfitLossRecord = new ProfitLossRecordPo();
        entryShippingProfitLossRecord.setCustomerId(this.getCustomerId());
        entryShippingProfitLossRecord.setContractId(this.getContractId());
        entryShippingProfitLossRecord.setBusinessBillId(this.getExitFormId());
        entryShippingProfitLossRecord.setFeeType(FeeTypeEnum.SHIPPING_FEE);
        entryShippingProfitLossRecord.setBusinessBillType(BusinessBillTypeEnum.EXIT);
        entryShippingProfitLossRecord.setStatus(ProfitLossStatusEnum.WAIT_EFFECTIVE);
        entryShippingProfitLossRecord.setTotalFeeAmount(this.getExitShippingAmount());
        entryShippingProfitLossRecord.setFeeIncurredTime(this.getTerminationTime());
        entryShippingProfitLossRecord.setId(identifierGenerator.nextId(entryShippingProfitLossRecord));
        this.profitLossRecordPoList.add(entryShippingProfitLossRecord);
        assembleDeviceProfitLossDetail(identifierGenerator, entryShippingProfitLossRecord.getId(), this.getExitShippingAmount());
    }

    /**
     * 获取待处理的进场运费减免业务记录以及设备改装费减免业务记录
     *
     * @return
     * @author songkf
     */
    public Wrapper<ProfitLossRecordPo> getPendingProfitLossRecordQueryWrapper() {
        return Wrappers.lambdaQuery(ProfitLossRecordPo.class)
                .eq(ProfitLossRecordPo::getContractId, contractId)
                .in(!allExitedEntryFormIdList.isEmpty(), ProfitLossRecordPo::getBusinessBillId, allExitedEntryFormIdList)
//                .eq(ProfitLossRecordPo::getStatus, ProfitLossStatusEnum.WAIT_EFFECTIVE)
                //费用类型为进场运费减免或设备改装费减免
                .in(ProfitLossRecordPo::getFeeType, FeeTypeEnum.SHIPPING_FEE_WAIVER, FeeTypeEnum.MODIFICATION_FEE_WAIVER)
                //费用发生日大于今天，只做日期比较，不做时间比较
                .gt(ProfitLossRecordPo::getFeeIncurredTime, this.getTerminationTime().toLocalDate());

    }

    /**
     * 组装设备损益明细记录
     *
     * @param identifierGenerator id生成器
     * @param profitLossRecordId  损益业务记录ID
     * @param totalAmount         总费用
     * @author songkf
     */
    private void assembleDeviceProfitLossDetail(DefaultIdentifierGenerator identifierGenerator, Long profitLossRecordId, BigDecimal totalAmount) {
        List<DeviceProfitLossDetailPo> deviceProfitLossDetailPoListShipping = new ArrayList<>(rentingDeviceList.size());
        BigDecimal totalMonthlyRentalPrice = rentingDeviceList.stream().map(DeviceRentalRecordPo::getMonthlyRentalPrice).reduce(BigDecimal.ZERO, BigDecimal::add);

        for (int i = 0; i < rentingDeviceList.size(); i++) {
            DeviceRentalRecordPo deviceRentalRecordPo = rentingDeviceList.get(i);
            BigDecimal dividedFee = totalAmount.multiply(deviceRentalRecordPo.getMonthlyRentalPrice()).divide(totalMonthlyRentalPrice, 5, RoundingMode.HALF_UP);
            if (i == rentingDeviceList.size() - 1) {
                dividedFee = totalAmount.subtract(deviceProfitLossDetailPoListShipping.stream().map(DeviceProfitLossDetailPo::getFee).reduce(BigDecimal.ZERO, BigDecimal::add));
            }
            ShippingInfoJson exitShippingInfo = deviceRentalRecordPo.getExitShippingInfo();
            if (exitShippingInfo != null) {
                exitShippingInfo.setShippingTotalAmount(dividedFee);
                deviceRentalRecordPo.setExitShippingInfo(exitShippingInfo);
            }
            deviceProfitLossDetailPoListShipping.add(assembleDeviceProfitLossRecord(identifierGenerator, profitLossRecordId, FeeTypeEnum.SHIPPING_FEE, deviceRentalRecordPo, dividedFee));
            assembleWaiverDeviceProfitLossDetail(dividedFee, identifierGenerator, profitLossRecordId, FeeTypeEnum.SHIPPING_FEE_WAIVER, deviceRentalRecordPo);
        }
        deviceProfitLossDetailPoList.addAll(deviceProfitLossDetailPoListShipping);
    }

    /**
     * 组装设备损益明细记录
     *
     * @param identifierGenerator  id生成器
     * @param profitLossRecordId   损益业务记录ID
     * @param businessBillType     业务单据类型
     * @param deviceRentalRecordPo 设备租赁记录
     * @param dividedFee           费用金额
     * @return 设备损益明细记录
     * @author songkf
     */
    private DeviceProfitLossDetailPo assembleDeviceProfitLossRecord(DefaultIdentifierGenerator identifierGenerator, Long profitLossRecordId, FeeTypeEnum businessBillType, DeviceRentalRecordPo deviceRentalRecordPo, BigDecimal dividedFee) {
        DeviceProfitLossDetailPo deviceProfitLossDetailPo = new DeviceProfitLossDetailPo();
        deviceProfitLossDetailPo.setDeviceRentalRecordId(deviceRentalRecordPo.getId());
        deviceProfitLossDetailPo.setProfitLossRecordId(profitLossRecordId);
        deviceProfitLossDetailPo.setBusinessBillType(BusinessBillTypeEnum.EXIT);
        deviceProfitLossDetailPo.setDeviceId(deviceRentalRecordPo.getDeviceId());
        deviceProfitLossDetailPo.setDeviceNo(deviceRentalRecordPo.getDeviceExtensionInfo().getDeviceNo());
        deviceProfitLossDetailPo.setContractId(deviceRentalRecordPo.getContractId());
        deviceProfitLossDetailPo.setContractNo(deviceRentalRecordPo.getContractNo());
        deviceProfitLossDetailPo.setFeeType(businessBillType);
        deviceProfitLossDetailPo.setFee(dividedFee);
        deviceProfitLossDetailPo.setId(identifierGenerator.nextId(deviceProfitLossDetailPo));
        deviceProfitLossDetailPo.setFeeIncurredTime(this.getTerminationTime());
        return deviceProfitLossDetailPo;
    }

    /**
     * 组装减免损益设备明细
     *
     * @param identifierGenerator 主键生成器
     * @param profitLossRecordId  损益业务记录ID
     * @param businessBillType    费用类型
     * @author songkf
     */
    private void assembleWaiverDeviceProfitLossDetail(BigDecimal divideFee, DefaultIdentifierGenerator identifierGenerator, Long profitLossRecordId, FeeTypeEnum businessBillType, DeviceRentalRecordPo deviceRentalRecordPo) {
        //遍历正在租赁的设备集合，判断在租设备的租赁天数是否符合减免条件，符合则将totalAmount分摊到每个设备上的单价添加到该设备的损益明细记录
        List<DeviceProfitLossDetailPo> deviceProfitLossDetailPoListShipping = new ArrayList<>(rentingDeviceList.size());
        //循环exitShippingFeeWaiverList
        for (WaiverRuleVo waiverRuleVo : exitShippingFeeWaiverList) {
            // 判断在租设备的租赁天数是否符合减免条件
            if (terminationTime.toLocalDate().toEpochDay() - deviceRentalRecordPo.getRentalStartTime().toLocalDate().toEpochDay() >= waiverRuleVo.getWaiverDays()) {

                BigDecimal unitPrice = divideFee
                        .multiply(BigDecimal.valueOf(waiverRuleVo.getWaiverRate()));

                // 添加到该设备的损益明细记录
                deviceProfitLossDetailPoListShipping.add(assembleDeviceProfitLossRecord(identifierGenerator, profitLossRecordId, businessBillType, deviceRentalRecordPo, unitPrice));
            }
        }
        deviceProfitLossDetailPoList.addAll(deviceProfitLossDetailPoListShipping);
    }

    /**
     * 获取检查设备租赁状态查询Wrapper
     *
     * @return 查询Wrapper
     * @author songkf
     */
    public Wrapper<DeviceRentalRecordPo> getCheckRentalStatusWrapper() {
        return Wrappers.lambdaQuery(DeviceRentalRecordPo.class)
                .in(DeviceRentalRecordPo::getDeviceId, exitDeviceInfoList.stream().map(DeviceInfoVo::getDeviceId).collect(Collectors.toList()))
                .in(DeviceRentalRecordPo::getEntryFormId, exitDeviceInfoList.stream().map(DeviceInfoVo::getEntryFormId).collect(Collectors.toList()))
                .isNotNull(DeviceRentalRecordPo::getRentalEndTime);
    }

    public void setExitShippingFeeWaiverList(List<DeviceRentalRecordPo> deviceRentalRecordPos) {
        DeviceRentalRecordPo deviceRentalRecordPo = deviceRentalRecordPos.get(0);
        deviceRentalRecordPo.getEntryShippingInfo().getShippingWaiverRuleList().forEach(waiverRuleVo -> {
            WaiverRuleVo vo = new WaiverRuleVo();
            vo.setWaiverDays(waiverRuleVo.getWaiverDays());
            vo.setWaiverRate(waiverRuleVo.getWaiverRate());
            this.exitShippingFeeWaiverList.add(vo);
        });
    }

    public Wrapper<DeviceProfitLossDetailPo> getPendingDeviceProfitLossDetailQueryWrapper() {
        return Wrappers.lambdaQuery(DeviceProfitLossDetailPo.class)
                .eq(DeviceProfitLossDetailPo::getContractId, contractId)
                .in(!rentingDeviceList.isEmpty(), DeviceProfitLossDetailPo::getDeviceRentalRecordId, rentingDeviceList.stream().map(DeviceRentalRecordPo::getId).collect(Collectors.toList()))
                .eq(DeviceProfitLossDetailPo::getBusinessBillType, BusinessBillTypeEnum.ENTRY)
                .in(DeviceProfitLossDetailPo::getFeeType, FeeTypeEnum.SHIPPING_FEE_WAIVER, FeeTypeEnum.MODIFICATION_FEE_WAIVER, FeeTypeEnum.PAUSE_FEE, FeeTypeEnum.INITIAL_DAY_FEE_WAIVER)
//                .eq(DeviceProfitLossDetailPo::getStatus, ProfitLossStatusEnum.WAIT_EFFECTIVE)
//                .gt(DeviceProfitLossDetailPo::getFeeIncurredTime, this.getTerminationTime().plusDays(1).toLocalDate());
                .ge(DeviceProfitLossDetailPo::getFeeIncurredTime, this.getTerminationTime().toLocalDate());

    }

    public RechargeTransactionBillPo assembleRechargeTransactionBill() {
        RechargeTransactionBillPo rechargeTransactionBill = new RechargeTransactionBillPo();
        rechargeTransactionBill.setContractId(this.getContractId());
        rechargeTransactionBill.setCustomerId(this.getCustomerId());
        rechargeTransactionBill.setContactorId(this.getRentingDeviceList().get(0).getCustomerExtensionInfo().getSignatoryId());
        rechargeTransactionBill.setRechargeType(RechargeTypeEnum.EXIT_RECHARGE);
        rechargeTransactionBill.setRechargeStatus(1);
        rechargeTransactionBill.setRechargeTotalAmount(BigDecimal.ZERO);
        List<RechargeTransactionBillPo.RelationBillInfo> relationBillInfoList = new ArrayList<>();
        this.getPendingDeviceDailyBillList().forEach(deviceDailyBillPo -> {
            RechargeTransactionBillPo.RelationBillInfo relationBillInfo = new RechargeTransactionBillPo.RelationBillInfo();
            relationBillInfo.setRelationBillId(deviceDailyBillPo.getId());
            relationBillInfo.setRelationBillType(1);
            relationBillInfo.setRechargeAmount(deviceDailyBillPo.getPaidAmount());
            relationBillInfoList.add(relationBillInfo);
        });
        this.getPendingContractDailyBillList().forEach(contractDailyBillPo -> {
            RechargeTransactionBillPo.RelationBillInfo relationBillInfo = new RechargeTransactionBillPo.RelationBillInfo();
            relationBillInfo.setRelationBillId(contractDailyBillPo.getId());
            relationBillInfo.setRelationBillType(2);
            relationBillInfo.setRechargeAmount(contractDailyBillPo.getTotalPaymentAmount());
            rechargeTransactionBill.setRechargeTotalAmount(rechargeTransactionBill.getRechargeTotalAmount().add(contractDailyBillPo.getTotalPaymentAmount()));
            relationBillInfoList.add(relationBillInfo);
        });
        rechargeTransactionBill.setRechargeDetailInfo(relationBillInfoList);
        rechargeTransactionBill.setCreatorId(-1L);
        rechargeTransactionBill.setCreatorName("系统自动触发");
        return rechargeTransactionBill;
    }
}
